package com.chen.seckill.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chen.seckill.config.AccessLimit;
import com.chen.seckill.exception.GlobalException;
import com.chen.seckill.pojo.*;
import com.chen.seckill.rabbitmq.MQSender;
import com.chen.seckill.resultvo.RespBean;
import com.chen.seckill.resultvo.RespBeanEnum;
import com.chen.seckill.service.IGoodsService;
import com.chen.seckill.service.IOrderService;
import com.chen.seckill.service.ISeckillOrderService;
import com.chen.seckill.utils.JsonUtil;
import com.wf.captcha.ArithmeticCaptcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * InitializingBean接口作用：在系统初始化时，执行afterPropertiesSet()方法，且只在系统初始化时，执行一次。
 */
@Slf4j
@Controller
@RequestMapping("/seckill")
public class SecKillController implements InitializingBean {

    @Resource
    private IGoodsService goodsService;

    @Resource
    private ISeckillOrderService seckillOrderService;

    @Resource
    private IOrderService orderService;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;


    @Resource
    private MQSender mqSender;

    @Resource
    private RedisScript<Long> redisScript;

    private Map<Long,Boolean> EmptyStockMap=new HashMap<>();


    /**
     * 秒杀功能
     * Windows优化前QPS:2298
     *
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
//    @RequestMapping("/doSeckill")
//    public String doSeckill(Model model, User user,Long goodsId){
//        //System.out.println(goodsId);
//        if (user==null){
//            System.out.println("user为空");
//            return "login";
//        }
//
//        model.addAttribute("user",user);
//        GoodsVO goodsVO = goodsService.findGoodsVOByGoodsId(goodsId);
//        //判断库存
//        if(goodsVO.getStockCount()<1){
//            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
//            return "secKillFail";
//        }
//        //判断是否重复抢购
//        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));
//        if (seckillOrder!=null){
//            model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
//            return "secKillFail";
//        }
//        Order order=orderService.seckill(user,goodsVO);
//        model.addAttribute("order",order);
//        model.addAttribute("goods",goodsVO);
//        return "orderDetail";
//    }

    /**
     * 秒杀功能
     * Windows优化前QPS:2298
     * （解决商品超卖，通过页面缓存，页面静态化，添加缓存 优化后的接口）优化后QPS:3600
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
   /* @RequestMapping(value = "/doSeckill",method = RequestMethod.POST)
    @ResponseBody
    public RespBean doSeckill(Model model, User user,Long goodsId){
       // System.out.println(user.getId());
        if (user==null){
            //System.out.println("user为空");
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        //model.addAttribute("user",user);
        GoodsVO goodsVO = goodsService.findGoodsVOByGoodsId(goodsId);
        //判断库存
        if(goodsVO.getStockCount()<1){
            //model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //判断是否重复抢购
        //SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if (seckillOrder!=null){
            //model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
        Order order=orderService.seckill(user,goodsVO);
        if (order!=null){
            return RespBean.success(order);
        }
        return RespBean.error(RespBeanEnum.ERROR);

    }*/

    /**
     * 秒杀功能(Redis预减库存)
     * Windows优化前QPS:2298
     * （解决商品超卖，通过页面缓存，页面静态化，添加缓存 优化后的接口）优化后QPS:3600
     *
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/{path}/doSeckill",method = RequestMethod.POST)
    @ResponseBody
    public RespBean doSeckill(@PathVariable String path, User user, Long goodsId){
        // System.out.println(user.getId());
        if (user==null){
            //System.out.println("user为空");
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Boolean check=orderService.checkPath(user,goodsId,path);
        if (!check){
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }
        //判断是否重复抢购
        //SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if (seckillOrder!=null){
            //model.addAttribute("errmsg",RespBeanEnum.REPEATE_ERROR.getMessage());
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
        /**
         * 内存标记
         * 减少Redis的访问
         *
         */
        if (EmptyStockMap.get(goodsId)){
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        /**
         * 预减库存
         * 在redis中，对key为"seckillGoods:" + goodsId的value值进行递减操作（每次都减一）
         * 得到每次秒杀后的库存
         */
        //Long stockCount = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
        /**
         * 预减库存优化（使用lua脚本，保证事务原子性）
         */
        Long stockCount=(Long)redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId),Collections.EMPTY_LIST);
        if (stockCount<1){
            EmptyStockMap.put(goodsId,true);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        SeckillMessage seckillMessage = new SeckillMessage();
        seckillMessage.setUser(user);
        seckillMessage.setGoodId(goodsId);
        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage));
        //Order order=orderService.seckill(user,goodsVO);
        return RespBean.success(0);

    }

    /**
     * 获取秒杀状态结果
     *
     * @param user
     * @param goodsId
     * @return orderId代表秒杀成功，-1代表秒杀失败，0代表排队中
     */
    @RequestMapping(value = "/result",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getResult(User user,Long goodsId){
        if (user==null){
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Long orderId=seckillOrderService.getResult(user,goodsId);
        return RespBean.success(orderId);
    }

    /**
     * 获取秒杀接口地址（主要是为了实现接口地址隐藏）
     * @AccessLimit为自定义注解
     * @param user
     * @param goodsId
     * @return
     */
    @AccessLimit(second=5,maxCount=5,needLogin=true)
    @RequestMapping(value = "/path",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getPath(User user, Long goodsId, String captcha, HttpServletRequest request){
        if (user==null){
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Boolean check=orderService.checkCaptcha(user,goodsId,captcha);
        if (!check){
            return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }
        String str=orderService.createPath(user,goodsId);
        return RespBean.success(str);
    }

    @RequestMapping(value = "/captcha",method = RequestMethod.GET)
    public void verifyCode(User user, Long goodsId, HttpServletResponse response){
        System.out.println(goodsId+"-----------------"+user);
        if (user==null||goodsId<0){
            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
        }
        //设置响应头输出类型为图片类型
        response.setContentType("image/jpg");
        response.setHeader("Pargam", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        //生成验证码，将结果放入redis中
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32, 3);
        redisTemplate.opsForValue().set("captcha:" + user.getId() + ":" + goodsId, captcha.text(), 300,
                TimeUnit.SECONDS);
        try {
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            log.error("验证码生成失败", e.getMessage());
        }
    }

    /**
     * 系统初始化时，把商品库存数量加载到Redis中
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVO> goodsVOList = goodsService.findGoodsVO();
        if (CollectionUtils.isEmpty(goodsVOList)){
            return;
        }
        for (GoodsVO goods:goodsVOList) {
            redisTemplate.opsForValue().set("seckillGoods:"+goods.getId(),goods.getStockCount());
            EmptyStockMap.put(goods.getId(),false);
        }
    }
}
